/* Copyright (c) 2009, Enric Rodriguez (enrodri86@gmail.com)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 	- Redistributions of source code must retain the above copyright
 * 	notice,	this list of conditions and the following disclaimer.
 * 	- Redistributions in binary form must reproduce the above copyright
 * 	notice, this list of conditions and the following disclaimer in the
 * 	documentation and/or other materials provided with the distribution.
 * 	- Neither the name of the <ORGANIZATION> nor the names of its
 * 	contributors may be used to endorse or promote products derived from
 * 	this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package es.enrodri86.mmo.auth.sgs;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;

import com.sun.sgs.kernel.ComponentRegistry;
import com.sun.sgs.service.Service;
import com.sun.sgs.service.TransactionProxy;

import es.enrodri86.mmo.auth.shared.Game;
import es.enrodri86.mmo.auth.shared.GameServer;

public class AuthService implements Service {

	private static final String SGSPORT_PROPERTY = "com.sun.sgs.app.port";
	private static final String SGSNAME_PROPERTY = "com.sun.sgs.app.name";

	private static final String GAME_ID_PROPERTY = AuthService.class.getCanonicalName()+".GameID";
	private static final String GAME_NAME_PROPERTY = AuthService.class.getCanonicalName()+".GameName";
	private static final String GAME_DESC_PROPERTY = AuthService.class.getCanonicalName()+".GameDesc";

	private static final String SERVER_ID_PROPERTY = AuthService.class.getCanonicalName()+".ServerID";
	private static final String SERVER_NAME_PROPERTY = AuthService.class.getCanonicalName()+".ServerName";
	private static final String SERVER_DESC_PROPERTY = AuthService.class.getCanonicalName()+".ServerDesc";
	private static final String SERVER_HOST_PROPERTY = AuthService.class.getCanonicalName()+".ServerHost";
	private static final String SERVER_PORT_PROPERTY = SGSPORT_PROPERTY;//AuthService.class.getCanonicalName()+".ServerPort";
	private static final String SERVER_URL_PROPERTY = AuthService.class.getCanonicalName()+".ServerURL";

	private static final String AUTH_SERVER_PORT_PROPERTY = AuthService.class.getCanonicalName()+".AuthServerPort";
	private static final String AUTH_SERVER_HOST_PROPERTY = AuthService.class.getCanonicalName()+".AuthServerHost";

	private Map<String, Object> sessionList;
	private AuthServerConnection conn;
	private final Game game;
	private String authServerHost;
	private int authServerPort;
	private GameServer gameServer;

	public AuthService(Properties properties, ComponentRegistry registry, TransactionProxy txnProxy) {
		String gameId = properties.getProperty(GAME_ID_PROPERTY);
		String gameName = properties.getProperty(GAME_NAME_PROPERTY);
		String gameDesc = properties.getProperty(GAME_DESC_PROPERTY);

		String gameServerId = properties.getProperty(SERVER_ID_PROPERTY);
		String gameServerName = properties.getProperty(SERVER_NAME_PROPERTY);
		String gameServerDesc = properties.getProperty(SERVER_DESC_PROPERTY);
		int gameServerStatus = GameServer.STATUS_RELOADING;
		String gameServerHost = properties.getProperty(SERVER_HOST_PROPERTY);
		int gameServerPort = Integer.parseInt(properties.getProperty(SERVER_PORT_PROPERTY));
		String gameServerURL = properties.getProperty(SERVER_URL_PROPERTY);

		authServerHost = properties.getProperty(AUTH_SERVER_HOST_PROPERTY);
		authServerPort = Integer.parseInt(properties.getProperty(AUTH_SERVER_PORT_PROPERTY));

		//Check game id
		if(gameId == null || gameId.equals(""))
			throw new NullPointerException("An ID for the game must be specified.");

		//Check game name
		if (gameName == null)
			gameName = properties.getProperty(SGSNAME_PROPERTY);
		if (gameName == null || gameName.equals(""))
			throw new NullPointerException("A name for the game must be specified.");

		//Check server id
		if(gameServerId == null || gameServerId.equals(""))
			throw new NullPointerException("An ID for the game server must be specified.");
		if (gameServerName == null || gameServerName.equals(""))
			throw new NullPointerException("A name for the game server must be specified.");

		//Check server port
		if(gameServerPort == 0)
			throw new NullPointerException("GameServer port must be specified.");
		//check server host
		if(gameServerHost == null || gameServerHost.equals(""))
			throw new NullPointerException("GameServer host must be specified.");

		sessionList = new Hashtable<String,Object>();

		gameServer = new GameServer();
		gameServer.setId(gameServerId);
		gameServer.setName(gameServerName);
		gameServer.setDescription(gameServerDesc);
		gameServer.setStatus(gameServerStatus);
		gameServer.setHost(gameServerHost);
		gameServer.setPort(gameServerPort);
		gameServer.setUrl(gameServerURL);

		game = new Game();
		game.setId(gameId);
		game.setName(gameName);
		game.setDescription(gameDesc);
		game.addServerID(gameServer.getId());

		conn = new AuthServerConnection();
		conn.start();


	}


	public String getName() {
		return this.getClass().getCanonicalName();
	}

	public void ready() throws Exception {
		// TODO Auto-generated method stub

	}

	public boolean shutdown() {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * Checks wether this session key is a valid one.
	 * @param skey
	 * @return
	 */
	public boolean checkSessionKey(String skey){
		return sessionList.containsKey(skey);
	}

	/**
	 * Returns the user ID binded to the sessoin key.
	 * @param skey
	 * @return
	 */
	public Object getUserID(String skey){
		Object t = sessionList.get(skey);
		// Removed because of "you are already playing" feature sessionList.remove(skey);
		return t;
	}

	/**
	 * Please, don't foreget to call this method when a client logs off in
	 * order to allow it connect again to the game.
	 * @param key
	 */
	public void userDisconnected(String key){
		sessionList.remove(key);
		conn.send(key);
	}

	public void setStatus(int status){
		gameServer.setStatus(status);
		conn.send(gameServer);
	}

	private class AuthServerConnection extends Thread{

		private static final long RETRY_DELAY = 5000;
		private ObjectOutputStream oos;

		public void send(Serializable g){
			if(oos != null){
				try {
					oos.writeObject(g);
					oos.flush();
				} catch (IOException e) {}
			}
		}

		public void run() {
			while(true){
				try {
					Socket s = new Socket(authServerHost,authServerPort);
					ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
					oos = new ObjectOutputStream(s.getOutputStream());
					send(game);
					send(gameServer);
					while(true){
						String key = null;
						Object value = null;
						try {
							key = (String) ois.readObject();
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						}
						try {
							value = ois.readObject();
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						}
						if(key != null && value != null){
							sessionList.put(key, value);
						}else{
							//TODO warn managers something gone wrong :S
						}

					}
				} catch (UnknownHostException e) {
					e.printStackTrace();
					try {sleep(RETRY_DELAY);} catch (InterruptedException e1) {}
					continue;
				} catch (IOException e) {
					e.printStackTrace();
					try {sleep(RETRY_DELAY);} catch (InterruptedException e1) {}
					continue;
				}
			}
		}
	}
}
